/**
 * Copyright 2010 Victor Egea Hernando
 *
 * RockScript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 * 
 * RockScript is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with RockScript.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "parser.h"

#include "rock_macros.h"

#include <fstream>
#include <iostream>

namespace rock
{

//
    class gc_ffile_hnd : public get_char_hnd
    {

    public:
        //
        void open( const char *path )
        {
            close();
            ifs_.open( path );

            if (!ifs_.is_open())
            {
                throw std::logic_error("parser error: file didn't open");
            }
        }
        //
        void close( void )
        {
            if (ifs_.is_open())
            {
                ifs_.close();
            }
        }
        //
        virtual char getc( void )
        {
            char c;
            ifs_.get(c);

            if ( ifs_.eof() )
            {
                ifs_.close();
                return 0;
            }

            return c;
        }
        //
        bool is_open( void )
        {
            return ifs_.is_open();
        }

    protected:
        //
        std::ifstream ifs_;

    };

    //
    class gc_fbuffer_hnd : public get_char_hnd
    {

    public:
        //
        void open( const char *buffer, corelib::uint32 size )
        {
            buffer_ = buffer;
            size_ = size;
            tellg_ = 0;
        }
        //
        virtual char getc( void )
        {
            if ( tellg_==size_ )
            {
                return 0;
            }

            char c = buffer_[tellg_];
            tellg_++;
            return c;
        }

    protected:
        //
        const char *buffer_;
        //
        corelib::uint32 tellg_, size_;

    };

    parser::parser( void )
    {
        reset();
        gcFBufferHnd_ = new gc_fbuffer_hnd();
        gcFFileHnd_ = new gc_ffile_hnd();
        lexer_.setErrorHnd( *this );
    }

    parser::~parser()
    {
        gcFFileHnd_->close();
        delete gcFBufferHnd_;
        delete gcFFileHnd_;
    }

    void parser::unexpectedTkErr( void )
    {
        std::stringstream ss;
        ss << "unexpected token '" << lexer_.tkt2str( ctk_.tk_.type_ ) << "'";
        ERROR_MSG( ss.str().c_str() );
    }

    void parser::expectedTkErr( corelib::uint32 type )
    {
        std::stringstream ss;
        ss << "token '" << lexer_.tkt2str( type ) << "' expected";
        ERROR_MSG( ss.str().c_str() );
    }

    void parser::feedFromFile( const char *path )
    {
        lexer_.setGetCharHnd( *gcFFileHnd_ );
        gcFFileHnd_->open( path );
    }

    void parser::feedFromBuffer( const char* buffer, corelib::uint32 size )
    {
        lexer_.setGetCharHnd( *gcFBufferHnd_ );
        gcFBufferHnd_->open( buffer, size );
    }

    void parser::lexError( corelib::uint32 line, const corelib::ustring &error )
    {
        errorHnd_->parserError(line,error);
    }

    void parser::setErrorHnd( parser_error_hnd &hnd )
    {
        errorHnd_ = &hnd;
    }

    bool parser::error( void ) const
    {
        return flags_ & FLG_COMPILER_ERROR;
    }

    void parser::setVMachine( paper::vmachine &vm )
    {
        vm_ = &vm;
    }

    corelib::ustring parser::unmangleMNames( const corelib::ustring &mstr )
    {
	std::string mname = mstr.str();
	// finding position
	size_t pos = 0, lpos = 0;

	// Class::mangledMethod_nargs_
	for(;;){
	   // slaughter prefix & sufix
	   if( ( ( pos = mname.find("meth_",lpos) )!=mname.npos ) || 
	       ( ( pos = mname.find("cntr_",lpos) )!=mname.npos ) || 
	       ( ( pos = mname.find("oper_",lpos) )!=mname.npos ) ) {
	        // slaughter prefix
	        mname = mname.substr( 0, pos ) + mname.substr( pos + 5 );
	        // slaughter sufix
	        if( ( lpos = mname.find("_",lpos) )!=mname.npos ){
	            mname = mname.substr( 0, lpos ) + "(" + mname[lpos+1] + ")" + mname.substr( lpos+3 );
	        }
	    }else{
	        break;
	    }
	}
	// return unmangled method names
	return mname;
    }

    void parser::parse( void ){
        try{
            // get first token
            ctk_.tk_ = lexer_.next();
            ctk_.line_ = lexer_.line();
            // call main block
            main_block();
            // check end of file
            if (ctk_.tk_.type_!=TK_EOF){
                flags_ |= FLG_COMPILER_ERROR;
                ERROR_MSG("the parser has ended before the end of file");
            }
        }catch ( std::logic_error &e ){
            flags_ |= FLG_COMPILER_ERROR;
        }catch ( std::exception &e ){
            flags_ |= FLG_COMPILER_ERROR;
            ERROR_MSG( std::string("critical exception<") + e.what() + std::string("> !") );
            throw e;
        }
    }

    void parser::reset( void )
    {
        pname_ = mname_ = cns_ = "";
        ac_ = corelib::null;
        am_ = corelib::null;
        lexer_.reset();
        flags_ = 0;
    }

    void parser::parseFStream( get_char_hnd &hnd )
    {
        lexer_.setGetCharHnd( hnd );
        parse();
    }

    void parser::parseFBuffer( const char *script, corelib::uint32 size )
    {
        feedFromBuffer( script, size );
        parse();
    }

    void parser::parseFFile( bool &is_open, const char *path )
    {
        feedFromFile( path );
        if ( is_open = gcFFileHnd_->is_open() )
        {
            parse();
        }
    }

    /*----------------------------------------------------------------
       Recursive Descent parser
      ----------------------------------------------------------------*/

    void parser::main_block( void )
    {
        if (ctk_.tk_.type_==TK_PACKAGE)
        {
            NEXT(); // TK_PACKAGE

            if (ctk_.tk_.type_==TK_COMMID)
            {
                pname_ = pname_ + *ctk_.tk_.val.s_;
                NEXT(); // TK_COMMID
            }

            while (ctk_.tk_.type_==TK_DOT)
            {
                pname_ = pname_ + ".";
                NEXT(); // TK_DOT

                if (ctk_.tk_.type_==TK_COMMID)
                {
                    pname_ = pname_ + *ctk_.tk_.val.s_;
                    NEXT(); // TK_COMMID
                }
            }

            // the current namespace is the package name
            cns_ = pname_;

            MATCH( TK_SEMC ); // ;
        }

        while (ctk_.tk_.type_==TK_IMPORT)
        {
            import_stm();
        }

        while (ctk_.tk_.type_==TK_CLASSID)
        {
            class_def();
        }
    }

    void parser::import_stm( void )
    {
        NEXT(); // TK_IMPORT

        MATCH( TK_CLASSID );

        while (ctk_.tk_.type_==TK_COM)
        {
            NEXT(); // TK_COM
            MATCH( TK_CLASSID );
        }

        MATCH( TK_FROM );
        MATCH( TK_COMMID );

        while (ctk_.tk_.type_==TK_DOT)
        {
            NEXT(); // TK_DOT
            MATCH( TK_COMMID );
        }

        MATCH( TK_SEMC ); // ;
    }

    void parser::class_def( void )
    {
        // save old namespace and old class
        corelib::ustring old_ns_ = cns_;
        paper::vm_class *old_ac_ = ac_;
		// set current namespace
		if( cns_.str().compare("")!=0 ){
			cns_ = cns_ + ".";
		}
        cns_ = cns_ + *ctk_.tk_.val.s_;
        // add class to virtual machine
        corelib::pair<paper::vm_class*, bool> ret = vm_->addClass( cns_ );
        // check if the class is already defined
        if ( ret.second==false ){
            ERROR( corelib::ustring("class '") + cns_ + corelib::ustring("' already defined") );
        }
        // point active class
        ac_ = ret.first;

        NEXT(); // TK_CLASSID

        if (ctk_.tk_.type_==TK_EXTENDS){
            NEXT(); // TK_EXTENDS

            while (ctk_.tk_.type_==TK_COMMID)
            {
                NEXT(); // TK_COMMID
                MATCH( TK_DOT );
            }

            MATCH( TK_CLASSID ); // Class
        }

        MATCH( TK_LCRLB ); // {

        bool done = false;
        while (!done){
            // clear state flags
            FLAGS_OFF(flags_, (~FLG_COMPILER_ERROR) );

            if (ctk_.tk_.type_==TK_STATIC){
                NEXT(); // TK_STATIC
                flags_ |= FLG_STATIC;
            }

            switch (ctk_.tk_.type_){
            case TK_LRNDB:
                if (flags_&FLG_STATIC){
                    ERROR("a constructor cannot be static");
                }
                flags_ |= FLG_CONSTRUCTOR;
                cnstr_def();
                break;
            case TK_COMMID:
                method_def();
                break;
            case TK_OPERATOR:
                flags_ |= FLG_OPERATOR;
                if (flags_&FLG_STATIC){
                    ERROR("an operator cannot be static");
                }
                operator_def();
                break;
            case TK_VAR:
                var_dec();
                break;
            default:
                if (flags_&FLG_STATIC){
                    ERROR("static must be followed by a method or variable");
                }else{
                    done = true;
                }
            }
        }

        // check if evry function has been defined
        std::vector<corelib::ustring> undefMethods = ac_->getUndefMethods();
        // throw an error if a method is not defined
        if ( undefMethods.size() ){
            // print undefined functions
            for (corelib::uint32 idx=0;idx<undefMethods.size();idx++){
                errorHnd_->parserError( ctk_.line_, corelib::ustring("the method '") +
            		undefMethods[idx] + corelib::ustring("' is used but not defined") );
            }
            // throw error
            throw std::logic_error("parser error");
        }

        // restore the old namespace and active class
        cns_ = old_ns_; ac_ = old_ac_;

        MATCH( TK_RCRLB ); // }
    }

    void parser::var_dec( void )
    {
        NEXT(); // TK_VAR
        if ( flags_&FLG_STATIC )
        {
            MATCH( TK_COMMID );
        }
        else
        {
            MATCH( TK_ATTRID );
        }

        while (ctk_.tk_.type_==TK_COM)
        {
            NEXT(); // TK_COM
            if ( flags_&FLG_STATIC )
            {
                MATCH( TK_COMMID );
            }
            else
            {
                MATCH( TK_ATTRID );
            }
        }

        MATCH( TK_SEMC );
    }

    void parser::cnstr_def( void )
    {
        NEXT(); // TK_CNSTR

        // get the nmber of arguments
        args_.clear(); corelib::uint32 nargs = 0;

        if (ctk_.tk_.type_==TK_COMMID)
        {
            MATCH( TK_COMMID );
            nargs++;

            while (ctk_.tk_.type_==TK_COM)
            {
                NEXT(); // TK_COM
                MATCH( TK_COMMID );
                nargs++;
            }
        }

        std::stringstream ss; ss << nargs;
        // set constructor name
        mname_ = corelib::ustring("cntr_") + ss.str() + "_";

        // add method to the class
        std::pair<paper::method*, bool> ret = ac_->addMethod( mname_ );
        // check if the method is already defined
        if ( ret.second==false && ( ret.first->flags() & paper::FLG_MTHD_DEFINED ) ){
            ERROR( corelib::ustring("method '") + mname_ + corelib::ustring("' already defined") );
        }
        // point active method & set flags
        am_ = ret.first;
        am_->setFlags( paper::FLG_MTHD_DEFINED );
		// set number of arguments
		if( !am_->setNArgs( nargs ) ){
			ERROR( corelib::ustring("method '") + mname_ + corelib::ustring("' has more than 9 arguments") );
		}

        MATCH( TK_RRNDB ); // )
        MATCH( TK_LCRLB ); // {

        // loop block body
        block_body();

        // add return for protection
		if( !am_->isLastOpRet() ){
			am_->addInstA(paper::OP_RETURN,0,0,0);
		}

        MATCH( TK_RCRLB ); // }
    }

    void parser::method_def( void )
    {
        neregs_ = nlvars_ = 0;
        mname_ = *ctk_.tk_.val.s_;

        NEXT(); // TK_COMMID

        MATCH( TK_LRNDB ); // (

        // get the number of arguments
        args_.clear();

        if (ctk_.tk_.type_==TK_COMMID){
            // insert class index if possible
            corelib::pair<argument_t::iterator,bool> res = 
	        args_.insert(argument_t::pair_t( *ctk_.tk_.val.s_, -args_.size()-2 ));

			if( !res.second ){
				ERROR( corelib::ustring("the argument '") + 
					*ctk_.tk_.val.s_ + corelib::ustring("' is repeated") );
			}

            NEXT(); // TK_COMMID

            while (ctk_.tk_.type_==TK_COM){
                NEXT(); // TK_COM

				if( ctk_.tk_.type_==TK_COMMID ){
					// insert class index if possible
					corelib::pair<argument_t::iterator,bool> res = 
						args_.insert(argument_t::pair_t( *ctk_.tk_.val.s_, -args_.size()-2 ));

					if( !res.second ){
						ERROR( corelib::ustring("the argument '") + 
						*ctk_.tk_.val.s_ + corelib::ustring("' is repeated") );
					}
				}

                MATCH( TK_COMMID );
            }
        }

        std::stringstream ss; ss << args_.size();
        // set constructor name
        mname_ = corelib::ustring("meth_") + mname_ + "_" + ss.str() + "_";

        // add method to the class
        std::pair<paper::method*, bool> ret = ac_->addMethod( mname_ );
        // check if the method is already defined
        if ( ret.second==false && ( ret.first->flags() & paper::FLG_MTHD_DEFINED ) ){
            ERROR( corelib::ustring("method '") + mname_ + corelib::ustring("' already defined") );
        }
        // point active method
        am_ = ret.first;
        // set flags
        if ( flags_&FLG_STATIC ){
            am_->setFlags( paper::FLG_MTHD_DEFINED | paper::FLG_MTHD_STATIC );
        }else{
            am_->setFlags( paper::FLG_MTHD_DEFINED );
        }
		// set number of arguments
		if( !am_->setNArgs( args_.size() ) ){
			ERROR( corelib::ustring("method '") + mname_ + corelib::ustring("' has more than 9 arguments") );
		}

        MATCH( TK_RRNDB ); // )
        MATCH( TK_LCRLB ); // {

        // loop block body
        block_body();

        // add return for protection
		if( !am_->isLastOpRet() ){
		        am_->addInstA(paper::OP_RETURN,0,0,0);
		}
        // set number of local variables and expression registers
        if( !am_->setStackFrameSize( nlvars_, neregs_ ) ){
	    	ERROR( corelib::ustring("method '") + mname_ + corelib::ustring("' stack frame is too large") );
		}

        MATCH( TK_RCRLB ); // }
    }

    void parser::operator_def( void )
    {
        NEXT(); // TK_OPERATOR

		args_.clear();

        // set constructor name
        mname_ = corelib::ustring("oper_");

        switch (ctk_.tk_.type_){
        case TK_OR: // |
        case TK_AND: // &
        case TK_XOR: // ^
        case TK_ADD: // +
        case TK_SUB: // -
        case TK_DIV: // /
        case TK_MLT: // *
        case TK_PRC: // %
        case TK_ARROP: // []
        case TK_EXCL: // !
        case TK_NOT: // ~
        case TK_DPLUS: // ++
        case TK_DSUB: // --
            break;
        default:
            UNEXPECTED();
        }

		corelib::uint32 nargs = 0;
        corelib::uint32 op = ctk_.tk_.type_;
		mname_ = mname_ + lexer::tkt2str( op );

        NEXT(); // operator

        MATCH( TK_LRNDB ); // (

        switch (op){
        case TK_OR: // |
        case TK_AND: // &
        case TK_XOR: // ^
        case TK_ADD: // +
        case TK_SUB: // -
        case TK_DIV: // /
        case TK_MLT: // *
        case TK_PRC: // %
        case TK_ARROP: // []
	    	mname_ = mname_ + "_1_"; nargs = 1;
            MATCH( TK_COMMID );
	    	break;
		default:
	    	mname_ = mname_ + "_0_"; 
        }

        // add method to the class
        std::pair<paper::method*, bool> ret = ac_->addMethod( mname_ );
        // check if the method is already defined
        if ( ret.second==false && ( ret.first->flags() & paper::FLG_MTHD_DEFINED ) ){
            ERROR( corelib::ustring("operator '") + mname_ + corelib::ustring("' already defined") );
        }
        // point active method
        am_ = ret.first;
        // set flags
        am_->setFlags( paper::FLG_MTHD_DEFINED );
		// set arguments
		am_->setNArgs( nargs );

        MATCH( TK_RRNDB ); // )
        MATCH( TK_LCRLB ); // {

        // loop block body
        block_body();

        // add return for protection
		if( !am_->isLastOpRet() ){
			am_->addInstA(paper::OP_RETURN,0,0,0);
		}
        MATCH( TK_RCRLB ); // }
    }

    void parser::var_def( void )
    {
        NEXT(); // TK_VAR

        for (;;)
        {
            nlvars_++;
            MATCH(TK_COMMID);
            // get expression
            if (ctk_.tk_.type_==TK_EQ)
            {
                NEXT(); // TK_EQ
                if (!expression())
                {
                    UNEXPECTED();
                }
		CLEAR_EXP();
            }
            // break loop or get next token and iterate again
            if (ctk_.tk_.type_==TK_COM)
            {
                NEXT(); // TK_COM
            }
            else
            {
                MATCH( TK_SEMC ); // ;
                return;
            }
        }
    }

    INLINE void parser::dowhile_loop( void )
    {
        bool loop_already = false;

        if (!(flags_&FLG_LOOP))
        {
            flags_ |= FLG_LOOP;
        }
        else
        {
            loop_already = true;
        }

        NEXT(); // TK_DO
        MATCH( TK_LCRLB ); // {

        block_body();

        MATCH( TK_RCRLB ); // }
        MATCH( TK_WHILE ); // while
        MATCH( TK_LRNDB ); // (

        if (!expression())
        {
            UNEXPECTED();
        }

        MATCH( TK_RRNDB ); // )
        MATCH( TK_SEMC ); // ;

        if (!loop_already)
        {
            flags_ &= (~FLG_LOOP);
        }
    }

    INLINE void parser::while_loop( void )
    {
        bool loop_already = false;

        if (!(flags_&FLG_LOOP))
        {
            flags_ |= FLG_LOOP;
        }
        else
        {
            loop_already = true;
        }

        NEXT(); // TK_WHILE
        MATCH( TK_LRNDB ); // (

        if (!expression())
        {
            UNEXPECTED();
        }

        MATCH( TK_RRNDB ); // )
        MATCH( TK_LCRLB ); // {

        // loop block body
        block_body();

        MATCH( TK_RCRLB ); // }

        if (!loop_already)
        {
            flags_ &= (~FLG_LOOP);
        }
    }

    INLINE void parser::foreach_loop( void )
    {
        bool loop_already = false;

        if (!(flags_&FLG_LOOP))
        {
            flags_ |= FLG_LOOP;
        }
        else
        {
            loop_already = true;
        }

        NEXT(); // TK_FOREACH
        MATCH( TK_LRNDB ); // (

        MATCH( TK_COMMID );
        MATCH( TK_IN );

        if (!expression())
        {
            UNEXPECTED();
        }

        MATCH( TK_RRNDB ); // )
        MATCH( TK_LCRLB ); // {

        // loop block body
        block_body();

        MATCH( TK_RCRLB ); // }

        if (!loop_already)
        {
            flags_ &= (~FLG_LOOP);
        }
    }

    INLINE void parser::switch_jumps( void )
    {
        bool switch_already = false;

        if (!(flags_&FLG_SWITCH))
        {
            flags_ |= FLG_SWITCH;
        }
        else
        {
            switch_already = true;
        }

        NEXT(); // TK_SWITCH
        MATCH( TK_LRNDB ); // (

        if (!expression())
        {
            UNEXPECTED();
        }

        MATCH( TK_RRNDB ); // )
        MATCH( TK_LCRLB ); // {

        block_body();

        MATCH( TK_RCRLB ); // }

        if (!switch_already)
        {
            flags_ &= (~FLG_SWITCH);
        }
    }

    INLINE void parser::if_else_blocks( void )
    {
        NEXT(); // TK_IF
        MATCH( TK_LRNDB ); // (

        if ( !expression() )
        {
            UNEXPECTED();
        }

        MATCH( TK_RRNDB ); // )
        MATCH( TK_LCRLB ); // {

        // loop block body
        block_body();

        MATCH( TK_RCRLB ); // }

        bool lastelse = false;

        while (!lastelse && ctk_.tk_.type_==TK_ELSE)
        {
            NEXT(); // TK_ELSE

            if ( ctk_.tk_.type_==TK_IF )
            {
                NEXT(); // TK_IF
                MATCH( TK_LRNDB ); // (

                if ( !expression() )
                {
                    UNEXPECTED();
                }

                MATCH( TK_RRNDB ); // )
            }
            else
            {
                lastelse = true;
            }

            MATCH( TK_LCRLB ); // {

            // loop block body
            block_body();

            MATCH( TK_RCRLB ); // }
        }
    }

    INLINE void parser::try_catch_block( void )
    {
        NEXT(); // TK_TRY
        MATCH( TK_LCRLB ); // {

        // loop block body
        block_body();

        MATCH( TK_RCRLB ); // }

        if ( ctk_.tk_.type_!=TK_CATCH )
        {
            UNEXPECTED();
        }

        do
        {
            NEXT(); // TK_CATCH
            MATCH( TK_LRNDB ); // (

            while (ctk_.tk_.type_==TK_COMMID)
            {
                NEXT(); // TK_COMMID
                MATCH( TK_DOT ); // .
            }

            MATCH( TK_CLASSID ); // Class
            MATCH( TK_COMMID ); // commid

            MATCH( TK_RRNDB ); // )
            MATCH( TK_LCRLB ); // {

            // loop block body
            block_body();

            MATCH( TK_RCRLB ); // }
        }
        while ( ctk_.tk_.type_==TK_CATCH );
    }

    void parser::block_body( void )
    {
	CLEAR_EXP();
        bool done = false;

        for (;;)
        {
            switch (ctk_.tk_.type_)
            {
            case TK_FOREACH:
                foreach_loop();
                break;
            case TK_WHILE:
                while_loop();
                break;
            case TK_DO:
                dowhile_loop();
                break;
            case TK_SWITCH:
                switch_jumps();
                break;
            case TK_TRY:
                try_catch_block();
                break;
            case TK_VAR:
                var_def();
                break;
            case TK_IF:
                if_else_blocks();
                break;
            case TK_THROW:
                NEXT(); // TK_THROW

                if (!expression())
                {
                    UNEXPECTED();
                }

                MATCH( TK_SEMC ); // ;
                break;
            case TK_RETURN:
                NEXT(); // TK_RETURN
                if( expression() ){
		    if( flags_ & FLG_CONSTRUCTOR ){
			ERROR("a constructor doesn't return a value");
		    }
        	    // add return
        	    am_->addInstA(paper::OP_RETURN,cregp_,0,0);
                }else{
		    if( flags_ & FLG_OPERATOR ){
			ERROR("an operator must return a value");
                    }
		     // add return
        	     am_->addInstA(paper::OP_RETURN,0,0,0);
                }
                MATCH( TK_SEMC ); // ;
		CLEAR_EXP();
                break;
            case TK_CASE:
                if ( !(flags_&FLG_SWITCH) )
                {
                    ERROR("'case' must be defined within a switch block");
                }

                NEXT(); // TK_CASE
                MATCH( TK_INT );
                MATCH( TK_COLON ); // :
                break;
            case TK_DEFAULT:
                if (!(flags_&FLG_SWITCH))
                {
                    ERROR("'default' must be defined within a switch block");
                }

                NEXT(); // TK_DEFAULT
                MATCH( TK_COLON ); // :
                break;
            case TK_CONTINUE:
                if (!(flags_&FLG_LOOP))
                {
                    ERROR("'continue' must be defined within a loop block");
                }

                NEXT(); //  TK_CONTINUE
                MATCH( TK_SEMC ); // ;
                break;
            case TK_BREAK:
                if (!(flags_&(FLG_SWITCH|FLG_LOOP)))
                {
                    ERROR("'break' must be defined within a switch or loop block");
                }

                NEXT(); // TK_BREAK
                MATCH( TK_SEMC ); // ;
                break;
            case TK_SEMC:
                NEXT(); // TK_SEMC
                break;
            default:
                if ( expression() ){
                    MATCH( TK_SEMC ); // ;
		    CLEAR_EXP();
                }else{
                    done = true;
                }
            }

            if (done) break;
        }
    }

    bool parser::expression( void )
    {
	// the return is stored in the register 0
	neregs_ = 1; 

        if(exp_branch())
        {
            for(;;)
            {
                switch (ctk_.tk_.type_)
                {
                case TK_SHFRUSEQ:
                case TK_SHFRSEQ:
                case TK_SHFLEQ:
                case TK_ANDEQ:
                case TK_XOREQ:
                case TK_ADDEQ:
                case TK_SUBEQ:
                case TK_MLTEQ:
                case TK_DIVEQ:
                case TK_MODEQ:
                case TK_OREQ:
                case TK_EQ:
                    NEXT();
                    if(!exp_branch())
                    {
                        ERROR("expression expected");
                    }
                    break;
                default:
                    return true;
                }
            }
        }
        return false;
    }

    bool parser::exp_branch( void )
    {
        if ( exp_logical() )
        {
            if (ctk_.tk_.type_==TK_QST)
            {
                NEXT(); // TK_QST

                if (!expression())
                {
                    ERROR("you must define an expression");
                }

                MATCH( TK_COLON ); // :

                if (!expression())
                {
                    ERROR("you must define an expression");
                }
            }
            return true;
        }
        return false;
    }

    bool parser::exp_logical( void )
    {
        if ( exp_or_xor_and() )
        {
            switch (ctk_.tk_.type_)
            {
            case TK_DAMP: // &&
            case TK_DVBAR: // ||
                NEXT();
                if (!exp_or_xor_and())
                {
                    UNEXPECTED();
                }
                return true;
            }
            return true;
        }
        return false;
    }

    bool parser::exp_or_xor_and( void )
    {
        if ( exp_equality() )
        {
            switch (ctk_.tk_.type_)
            {
            case TK_OR: // |
            case TK_XOR: // ^
            case TK_AND: // &
                NEXT();
                if (!exp_equality())
                {
                    UNEXPECTED();
                }
                return true;
            }
            return true;
        }
        return false;
    }

    bool parser::exp_equality( void )
    {
        if ( exp_relational() )
        {
            switch (ctk_.tk_.type_)
            {
            case TK_DEQ: // ==
            case TK_NE: // !=
                NEXT();
                if (!exp_relational())
                {
                    UNEXPECTED();
                }
                return true;
            }
            return true;
        }
        return false;
    }

    bool parser::exp_relational( void )
    {
        if ( exp_shift() )
        {
            switch (ctk_.tk_.type_)
            {
            case TK_LESSEQ: // <=
            case TK_GRTEQ: // =>
            case TK_LESST: // <
            case TK_GRTT: // >
                NEXT();
                if (!exp_shift())
                {
                    UNEXPECTED();
                }
                return true;
            }
            return true;
        }
        return false;
    }

    bool parser::exp_shift( void )
    {
        if ( exp_in_instof() )
        {
            switch (ctk_.tk_.type_)
            {
            case TK_SHFL: // <<
            case TK_SHFRS: // >>
            case TK_SHFRUS: // >>>
                NEXT();
                if (!exp_in_instof())
                {
                    UNEXPECTED();
                }
                return true;
            }
            return true;
        }
        return false;
    }

    bool parser::exp_in_instof( void )
    {
        if ( exp_simple() )
        {
            switch (ctk_.tk_.type_)
            {
            case TK_IN: // in
                NEXT(); // TK_IN
                if (!exp_simple())
                {
                    UNEXPECTED();
                }
                return true;
            case TK_INSTOF: // instanceof
                NEXT(); // TK_INSTOF

                while (ctk_.tk_.type_==TK_COMMID)
                {
                    NEXT(); // TK_COMMID
                    MATCH( TK_DOT );
                }

                MATCH( TK_CLASSID ); // Class

                return true;
            }
            return true;
        }
        return false;
    }

    bool parser::exp_simple( void )
    {
        if ( term() ){
            bool done = false; 
            while (!done)
            {
                switch (ctk_.tk_.type_)
                {
                case TK_ADD:{
                    NEXT();
		    // get right factor
                    if ( !term() ) UNEXPECTED();
		    am_->addInstA(paper::OP_ADD,cregp_,expp_,expp_);
		    cregp_ = expp_; if( expp_>1 ) POP_EXP();
                    break;
                }case TK_SUB:{
                    NEXT();
                    if ( !term() ) UNEXPECTED();
                    break;
                }default:
                    done = true;
                }
            }
            return true;
        }
        return false;
    }

    bool parser::term( void )
    {
        if ( factor() )
        {
            bool done = false;
            while (!done)
            {
                switch (ctk_.tk_.type_)
                {
                case TK_MLT:{
                    NEXT();
		    // save current position
		    corelib::int16 oregp = cregp_; 
		    cregp_++; PUSH_EXP();
		    // get right factor
                    if ( !factor() ) UNEXPECTED();
		    am_->addInstA(paper::OP_MLT,oregp,cregp_,expp_);
		    // restore old position
		    cregp_ = oregp; POP_EXP();
                    break;
                }case TK_DIV:{
                    NEXT();
                    if ( !factor() ) UNEXPECTED();
		    am_->addInstA(paper::OP_DIV,0,0,0);
                    break;
                }case TK_PRC:{
                    NEXT();
                    if ( !factor() ) UNEXPECTED();
		    am_->addInstA(paper::OP_DIV,0,0,0);
                    break;
                }default:
                    done = true;
                }
            }
            return true;
        }
        return false;
    }

    bool parser::factor( void )
    {
        corelib::uint32 pre_op = 0;

        switch (ctk_.tk_.type_)
        {
        case TK_DPLUS: // ++
            pre_op = TK_DPLUS;
            NEXT(); // token
            break;
        case TK_DSUB: // --
            pre_op = TK_DSUB;
            NEXT(); // token
            break;
        case TK_SUB: // -
            pre_op = TK_SUB;
            NEXT(); // token
            break;
        case TK_EXCL: // !
            pre_op = TK_EXCL;
            NEXT(); // token
            break;
        case TK_NOT: // ~
            pre_op = TK_NOT;
            NEXT(); // token
            break;
        }

        if (ctk_.tk_.type_==TK_LRNDB)
        {
            NEXT(); // TK_LRNDB

            if (!expression())
            {
                UNEXPECTED();
            }

            MATCH( TK_RRNDB ); // )

            if (!post_op(pre_op))
            {
                if (ctk_.tk_.type_==TK_DOT)
                {
                    method_calls(pre_op);
                }
            }

            return true;
        }
        else
        {
            if (language_obj(pre_op))
            {
                return true;
            }

            if (obj_instance())
            {
                if (!post_op(pre_op))
                {
                    if (ctk_.tk_.type_==TK_DOT)
                    {
                        method_calls(pre_op);
                    }
                }
                return true;
            }

            return false;
        }
    }

    bool parser::post_op( corelib::uint32 pre_op )
    {
        // ([ expression() ])*
        while (ctk_.tk_.type_==TK_LSQRB)
        {
            NEXT(); // [

            if (!expression())
            {
                UNEXPECTED();
            }

            MATCH( TK_RSQRB ); // ]
        }

        // (++|--)?
        switch (ctk_.tk_.type_)
        {
        case TK_DPLUS:
        case TK_DSUB:
            if (pre_op)
            {
                ERROR_TK(pre_op, "operator cannot be used twice around" );
            }
            NEXT(); // token
            return true;
        default:
            return false; // epsilon
        }
    }

    bool parser::language_obj( corelib::uint32 pre_op )
    {
        switch (ctk_.tk_.type_)
        {
        case TK_INT:
            switch (pre_op)
            {
            case 0:
            case TK_NOT: // ~
            case TK_DPLUS: // ++
            case TK_DSUB: // --
                break;
            default:
                ERROR_TK(pre_op," not allowed with integer constant");
            }
	    // we can load small integers directly with a special opcode
	    if( ctk_.tk_.val.i_ > -128 && ctk_.tk_.val.i_ < 128 ){
	    	am_->addInstB(paper::OP_LOADI,ctk_.tk_.val.i_,expp_);
	    }else{

	    }
            NEXT();
            return true;
        case TK_REAL:
            if (pre_op!=0)
            {
                ERROR_TK(pre_op," not allowed with real constant");
            }
            NEXT();
            return true;
        case TK_STRING:
            if (pre_op!=0)
            {
                ERROR_TK(pre_op," not allowed with string constant");
            }
            NEXT();
            return true;
        case TK_NULL:
            if (pre_op!=0)
            {
                ERROR_TK(pre_op," not allowed with null constant");
            }
            NEXT();
            return true;
        case TK_TRUE:
        case TK_FALSE:
            switch (pre_op)
            {
            case 0:
            case TK_EXCL: // !
                break;
            default:
                ERROR_TK(pre_op," not allowed with boolean constant");
            }
            NEXT();
            return true;
        default:
            return false;
        }
    }

    bool parser::obj_instance( void )
    {
        switch (ctk_.tk_.type_)
        {
        case TK_COMMID:{
            // get the name
            corelib::ustring mname = *ctk_.tk_.val.s_;

            NEXT(); // TK_COMMID

            if (ctk_.tk_.type_==TK_DOT)
            {
                NEXT(); // TK_DOT

                /*
                 * package.ClassId()
                 * package.ClassId::method()
                 */
                if (ctk_.tk_.type_==TK_CLASSID)
                {
                    NEXT(); // TK_CLASSID
                    class_instance();
                    return true;
                }

                MATCH( TK_COMMID );

                // variable.methodId();
                if (ctk_.tk_.type_==TK_LRNDB)
                {
                    call_arguments();
                    return true;
                }

                while (ctk_.tk_.type_==TK_DOT)
                {
                    NEXT(); // TK_DOT

                    /*
                     * package.(package.)+ClassId()
                     * package.(package.)+ClassId::method()
                     */
                    if (ctk_.tk_.type_==TK_CLASSID)
                    {
                        NEXT(); // TK_CLASSID
                        class_instance();
                        return true;
                    }

                    MATCH( TK_COMMID );
                }

                ERROR("you should specify a class id");
            }
            else
            {
                if (ctk_.tk_.type_==TK_LRNDB)
                {
                    // methodId();
                    corelib::uint32 nargs = call_arguments();
		    // creame method name
        	    std::stringstream ss; ss << nargs;
        	    // set constructor name
        	    mname = corelib::ustring("meth_") + mname + "_" + ss.str() + "_";
                    // get method index; create it otherwise
                    corelib::uint32 idx;
                    // add method if it's not added already
                    ac_->addMethod(mname);
                    // get index
                    ac_->getMethodIdx(mname, idx);
		    // move old returning register if necessary
		    if( !cregp_ ){
			am_->addInstB( paper::OP_LOAD,0,expp_ ); 
		        /* the last value is returned in expp_;
			 * operation inverted; we must submit this 
			 */
		        //cregp_ = expp_; PUSH_EXP();
		    }else{
		        // the last value is returned in 0
		        cregp_ = 0;
		    }
                    // add call opcode: It's a known class at runtime
                    am_->addInstB( paper::OP_KC_CALL,idx,ac_->idx() );
                }else{
		    argument_t::iterator it = args_.find( mname_ );
		    // check if it's an argument
		    if( it!=args_.end() ){
			cregp_ = it->second;
		    }else{
			// variable
		    }
                }
                return true;
            }
        }case TK_ATTRID:{
            if ( flags_&FLG_STATIC )
            {
                UNEXPECTED();
            }
            else
            {
                NEXT(); //TK_ATTRID
                // attribute_

                return true;
            }
        }case TK_CLASSID:{
            NEXT(); // TK_CLASSID
            /*
             * ClassId()
             * ClassId::method()
             */
            class_instance();
            return true;
        }case TK_THIS:{
            NEXT(); // TK_THIS
            return true;
        }default:
            return false;
        }
    }

    void parser::class_instance( void )
    {
        if ( ctk_.tk_.type_==TK_DCOLON )
        {
            NEXT(); // TK_DCOLON
            MATCH(TK_COMMID);

            if (ctk_.tk_.type_==TK_LRNDB)
            {
                // static method call
                call_arguments();


            }
            else
            {
                UNEXPECTED();
            }
        }
        else
        {
            if (ctk_.tk_.type_==TK_LRNDB)
            {
                // constructor call
                call_arguments();


            }
            else
            {
                UNEXPECTED();
            }
        }
    }

    void parser::method_calls( corelib::uint32 pre_op )
    {
        bool postop = false;

        do
        {
            if (postop)
            {
                ERROR( "you cannot call a method after using a decrement/increment operator" );
            }

            NEXT(); // TK_DOT
            MATCH( TK_COMMID );
            if (ctk_.tk_.type_==TK_LRNDB)
            {
                call_arguments();
            }
            else
            {
                UNEXPECTED();
            }

            postop = post_op(pre_op);
        }
        while (ctk_.tk_.type_==TK_DOT);
    }

    corelib::uint32 parser::call_arguments( void )
    {
        NEXT(); // TK_LRNDB

        // get the number of arguments
        corelib::uint32 nargs = 0;

        if ( expression() )
        {
	    am_->addInstA(paper::OP_PUSHARGS,cregp_,1,0);
            nargs++;
            while (ctk_.tk_.type_==TK_COM)
            {
                NEXT(); // TK_COM
                if ( !expression() )
                {
                    UNEXPECTED();
                }
		
	    	am_->addInstA(paper::OP_PUSHARGS,cregp_,1,0);
		nargs++;
            }
        }

        MATCH( TK_RRNDB ); // )
	return nargs;
    }

}; // namespace rock
